import java.awt.geom.*;
import java.awt.*;
import java.awt.font.TextAttribute;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;

/**
 * Clase que extiende <b>Rubber</b> para así desplegar elasticos en pantalla. La apariencia
 * de un elastico es el de una linea recta.
 *
 */
class GRubber extends Rubber implements Drawable {
   private static final double xPoints[]={0,0.5,1};
   private static final double yPoints[]={0,0,0};
   private static final Path2D.Double polyline =
                      new Path2D.Double(Path2D.WIND_EVEN_ODD,xPoints.length);
   private Path2D.Double shape;
   private Stroke stroke;
   private String stiffval;
   private Vector2D freeAendPosition;    // to draw a spring we need
   private Vector2D freeBendPosition;  // extremes well definied
                                     // when it is not connected.

   static {  // static initialization block. It creates a rubber of length = 1.
      polyline.moveTo(xPoints[0], yPoints[0]);
      polyline.lineTo(xPoints[0], yPoints[0]+0.025);
      polyline.lineTo(xPoints[0], yPoints[0]-0.025);
      polyline.moveTo(xPoints[0], yPoints[0]);
      polyline.lineTo(xPoints[1],yPoints[1]);
      polyline.moveTo(xPoints[1],yPoints[1]);
      polyline.lineTo(xPoints[2],yPoints[2]);
      polyline.moveTo(xPoints[2],yPoints[2]);
      polyline.lineTo(xPoints[2],yPoints[2]+0.025);
      polyline.lineTo(xPoints[2],yPoints[2]-0.025);
   }


   /**
    *
    * @param restLength largo en reposo
    * @param stiffness constante elastica del elastico
    */
   public GRubber(float restLength, float stiffness) {
      super(restLength, stiffness);
      freeAendPosition = new Vector2D(0,0);
      freeBendPosition = new Vector2D(restLength,restLength);
      AffineTransform at = AffineTransform.getTranslateInstance(0,0);
      Vector2D v = getVector();
      at.rotate(v.getX(), v.getY());
      at.scale(v.module(), restLength);
      shape = (Path2D.Double) at.createTransformedShape(polyline);
      stroke = new BasicStroke(0.01f);
   }
   /**
    * obtiene la posicion de la punta A del elastico.
    * @return posicion de reposo en el caso que no tenga nada atado a la punta A,
    * o la posicion del objeto atado a la posicion A.
    */
   public Vector2D getAendPosition() {
      if (a_end == null)
         return freeAendPosition;
      return super.getAendPosition();
   }
   /**
    * obtiene la posicion de la punta B del elastico.
    * @return posicion de reposo en el caso que no tenga nada atado a la punta B,
    * o la posicion del objeto atado a la posicion B.
    */
   public Vector2D getBendPosition() {
      if (b_end == null)
         return freeBendPosition;
      return super.getBendPosition();
   }
   /**
    * dibuja en pantalla el objeto rubber. recupera el valor de stiffness para desplegarlo
    * en pantalla, dentro de un rectangulo de color negro. Este cambia
    * de tamaño dinamicamente para adaptarse al tamaño del string que representa
    * al stiffness.
    * @param g
    */
   public void draw (Graphics2D g){
      stiffval = ""+get_stiffness();
      Vector2D a=getAendPosition();
      double ax = a.getX();
      double ay = a.getY();
      Vector2D v = getVector();
      AffineTransform at = AffineTransform.getTranslateInstance(ax, ay);
      at.rotate(v.getX(), v.getY());
      at.scale(v.module(),  restLength);
      shape = (Path2D.Double) at.createTransformedShape(polyline);
      if (v.module() < restLength)
         g.setColor(Color.BLACK);
      else
         g.setColor(Color.RED);
      g.setStroke(stroke);
      g.draw(shape);
      g.setColor(Color.BLACK);
      g.fill(new Rectangle2D.Float((float)(v.getX()/2 + ax-0.03),
                                   (float) ((v.getY()/2 + ay)-0.02),
                                   (float) ((stiffval.length()-1)*0.1+0.06),(float)0.15));

      AffineTransform at2 = new AffineTransform();
      at2.scale(0.015, -0.015);
      AttributedString styledText = new AttributedString(stiffval);
      styledText.addAttribute(TextAttribute.FOREGROUND, new Color(0,255,0));
      styledText.addAttribute(TextAttribute.FAMILY,Font.DIALOG_INPUT);
      styledText.addAttribute(TextAttribute.WEIGHT,TextAttribute.WEIGHT_BOLD);
      styledText.addAttribute(TextAttribute.WIDTH,TextAttribute.WIDTH_CONDENSED);
      styledText.addAttribute(TextAttribute.TRANSFORM, at2);
      AttributedCharacterIterator iter = styledText.getIterator();
      g.drawString(iter, (float) (v.getX()/2 + ax),
                             (float) (v.getY()/2 + ay)
                             );
      
   }
   /**
    * Consulta si el punto (x,y) pertenece al elastico.
    * @param x
    * @param y
    * @return true, si el punto (x,y) pertenece al elastico, y falso en caso contrario.
    */
   public boolean contains(double x, double y){
      return shape.getBounds2D().contains(x,y);
   }
   /**
    * Arrastra el elastico hasta la posicion (x,y).
    * @param x
    * @param y
    */
   public void dragTo(double x, double y){
      Vector2D a=getAendPosition();
      Vector2D b=getBendPosition();
      double da = Math.abs(a.getX()-x)+Math.abs(a.getY()-y); // "distance" to a
      double db = Math.abs(b.getX()-x)+Math.abs(b.getY()-y); // "distance" to a
      if (da < db) {
         detachAend();
         freeAendPosition.set(x,y);
      }
      else {
         detachBend();
         freeBendPosition.set(x,y);
      }
   }
   /**
    * Actualiza el estado del elastico, de acuerdo a las posiciones actuales de sus puntas.
    */
   public void updateState(){  // just to return to a rest length
      if (a_end==null && b_end==null) return;  // when it is released
      if (a_end!=null) {
         Vector2D v = getVector().unitary().times(restLength);
         freeBendPosition = getAendPosition().plus(v);
         return;
      }
      if (b_end!=null) {
         Vector2D v = getVector().unitary().times(restLength);
         freeAendPosition = getBendPosition().minus(v);
         return;
      }
   }
   /**
    * Pinta al elastico como seleccionado.
    */
   public void setSelected() {
      stroke = new BasicStroke(0.03f);
   }
   /**
    * Pinta al elastico como deseleccionado.
    */
   public void setReleased() {
      stroke = new BasicStroke(0.01f);
   }
}
